import os
import traceback
from datetime import datetime, timedelta
from enum import Enum

class LogError(Exception):
    def __init__(self, msg):
        self.msg = msg
        
    def __str__(self):
        return self.msg


class LogLevel(Enum):
    LOG = 0
    MSG = 1
    NOTICE = 2
    WARNING = 3
    ERR = 4
    FATAL = 5

    @staticmethod
    def get_level_by_str(string):
        for level in LogLevel:
            if string.lower() == level.name.lower():
                return level
        raise PorjectError('unknown level string ' + string)


class Log(object):

    def __init__(self, dir, logname='run.log', use_header=True, use_split=True):
        self.file = os.path.join(dir, logname)
        self.use_header = use_header
                
        self._file = open(self.file, 'a', encoding='utf-8')
        self._lowest_print_level = LogLevel.NOTICE
        # just some flag
        if use_split:
            self._file.write('\n' * 10)
            self._file.write('#' * 30)
            self._file.write('\n>>>>>>>>>> NEW LOG START <<<<<<<<<<\n')

    def __del__(self):
        self._file.close()
        
    def setLowestPrintLevel(self, level):
        self._lowest_print_level = level

    def showLowestPrintLevel(self, ):
        return self._lowest_print_level
    
    def print_msg(self, msg, level, end='\n'):
        if level.value >= self._lowest_print_level.value or level == LogLevel.MSG:
            print(msg, end=end)
    
    def do_log(self, level, content):
        now = datetime.now()
        formatted_time = now.strftime("%Y-%m-%d %H:%M:%S.%f")
        level_tag = '[{}]'.format(level.name)
        log_content = (content + '\n') if level == LogLevel.MSG else \
                      '{0} {1}: {2}\n'.format(formatted_time, level_tag, content)

        self._file.write(log_content)
        self._file.flush()
        if level == LogLevel.LOG:
            self.print_msg(log_content, level, end='')

        elif level == LogLevel.NOTICE:
            self.print_msg(log_content, level, end='')

        elif level == LogLevel.MSG:
            self.print_msg(log_content, level, end='')

        elif level == LogLevel.WARNING:
            self.print_msg(log_content, level, end='')

        elif level == LogLevel.ERR:
            bt = ''.join(traceback.format_stack()[0:-1])
            self.print_msg(log_content + '\n' + bt, level, end='')
            raise LogError(content + '\n' + bt)

        elif level == LogLevel.FATAL:
            bt = ''.join(traceback.format_stack()[0:-1])
            self.print_msg(log_content + '\n' + bt, level, end='')
            exit(1)

        else:
            assert False
            
    def log(self, content):
        self.do_log(LogLevel.LOG, content)
    
    def notice(self, content):
        self.do_log(LogLevel.NOTICE, content)

    def msg(self, content):
        self.do_log(LogLevel.MSG, content)
        
    def warning(self, content):
        self.do_log(LogLevel.WARNING, content)
        
    def err(self, content):
        self.do_log(LogLevel.ERR, content)
        
    def fatal(self, content):
        self.do_log(LogLevel.FATAL, content)
        
